package logger

import (
	"os"
	"strings"
	"time"

	"gitee.com/huangxinbo/xcas/internal/config"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
)

const (
	XRequestId = "X-Request-Id"
)

type Logger struct {
	Logger     *zap.Logger
	XRequestId string
}

// NewLogger initializes logger
func NewLogger(module string, config config.Config, opts ...zap.Option) (*Logger, error) {
	// Create log directory
	logDir := config.Log.Path + module
	if err := os.MkdirAll(logDir, 0755); err != nil {
		return nil, err
	}

	// Set log level
	var level zapcore.Level
	switch strings.ToLower(config.Log.Level) {
	case "debug":
		level = zapcore.DebugLevel
	case "info":
		level = zapcore.InfoLevel
	case "warn", "warning":
		level = zapcore.WarnLevel
	case "error":
		level = zapcore.ErrorLevel
	case "fatal":
		level = zapcore.FatalLevel
	case "panic":
		level = zapcore.PanicLevel
	default:
		level = zapcore.InfoLevel
	}

	// Encoder configuration
	encoderConfig := zapcore.EncoderConfig{
		TimeKey:        "time",
		LevelKey:       "level",
		NameKey:        "logger",
		CallerKey:      "caller",
		MessageKey:     "msg",
		StacktraceKey:  "stacktrace",
		LineEnding:     zapcore.DefaultLineEnding,
		EncodeLevel:    zapcore.CapitalLevelEncoder,
		EncodeTime:     zapcore.ISO8601TimeEncoder,
		EncodeDuration: zapcore.SecondsDurationEncoder,
		EncodeCaller:   zapcore.ShortCallerEncoder,
	}

	// Create custom rotator
	rotator := NewCustomRotator(logDir, config.Log.MaxSize, config.Log.MaxBackups, config.Log.MaxAge)

	// Create core
	core := zapcore.NewCore(
		zapcore.NewJSONEncoder(encoderConfig),
		zapcore.NewMultiWriteSyncer(
			zapcore.AddSync(rotator),
			zapcore.AddSync(os.Stdout), // Also output to console
		),
		level,
	)

	// Create logger
	zapLogger := zap.New(core, zap.AddCaller(), zap.AddStacktrace(zapcore.ErrorLevel))
	if len(opts) > 0 {
		zapLogger = zapLogger.WithOptions(opts...)
	}

	return &Logger{
		// Config: config,
		Logger: zapLogger,
	}, nil
}

func (l *Logger) Close() error {
	return l.Logger.Sync()
}

func (l *Logger) Debug(msg string, fields ...zap.Field) {
	if l.XRequestId != "" {
		fields = append(fields, zap.String(XRequestId, l.XRequestId))
	}
	// Skip 1 caller to show the actual caller location instead of logger/logger.go
	l.Logger.WithOptions(zap.AddCallerSkip(1)).Debug(msg, fields...)
}

func (l *Logger) Info(msg string, fields ...zap.Field) {
	if l.XRequestId != "" {
		fields = append(fields, zap.String(XRequestId, l.XRequestId))
	}
	// Skip 1 caller to show the actual caller location instead of logger/logger.go
	l.Logger.WithOptions(zap.AddCallerSkip(1)).Info(msg, fields...)
}

func (l *Logger) Warn(msg string, fields ...zap.Field) {
	if l.XRequestId != "" {
		fields = append(fields, zap.String(XRequestId, l.XRequestId))
	}
	// Skip 1 caller to show the actual caller location instead of logger/logger.go
	l.Logger.WithOptions(zap.AddCallerSkip(1)).Warn(msg, fields...)
}

func (l *Logger) Error(msg string, fields ...zap.Field) {
	if l.XRequestId != "" {
		fields = append(fields, zap.String(XRequestId, l.XRequestId))
	}
	// Skip 1 caller to show the actual caller location instead of logger/logger.go
	l.Logger.WithOptions(zap.AddCallerSkip(1)).Error(msg, fields...)
}

func (l *Logger) Fatal(msg string, fields ...zap.Field) {
	if l.XRequestId != "" {
		fields = append(fields, zap.String(XRequestId, l.XRequestId))
	}
	// Skip 1 caller to show the actual caller location instead of logger/logger.go
	l.Logger.WithOptions(zap.AddCallerSkip(1)).Fatal(msg, fields...)
}

// getLogFileName gets log filename, format is YYYYMMDD.log
func getLogFileName() string {
	return time.Now().Format("20060102") + ".log"
}
